题意
给定 $n$ 个线段,每个线段有一个权值 $w_i$,每次取轴上一点,获得的权值为选择的覆盖在当前点上的线段的最大权值,求最终覆盖所有线段后需要的最小权值
题解
好吧是一道非常水的区间 $dp$ 然而我并没有想出来所以我是真的绝望
先把 $l_i, r_i$ 离散化后令 $f_{l, r}$ 表示完全覆盖区间 $[l, r]$ 的线段的最小权值,设完全处于 $[l, r]$ 的线段中权值取最大值的线段 $p$,则有转移方程
$$
f_{l, r} = \min \{f_{l, k - 1} + f_{k + 1, r} + w_p\} (l_p \le k \le r_p)
$$
所以注意一定要完全覆盖,$[l, k - 1], [k + 1, r]$ 才不会对 $[l, r]$ 造成影响,于是我就卡这儿了
谨以此文纪念我逝去的智商
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| #include <iostream> #include <cstdio> #include <cstring> #include <algorithm>
using namespace std;
const int MAXN = 600 + 10;
const int INF = 0x3f3f3f3f;
int f[MAXN][MAXN]= {0};
int T; int N; int l[MAXN], r[MAXN], value[MAXN]; int vcol[MAXN << 1], vcnt = 0;
int getnum () { int num = 0; char ch = getchar ();
while (! isdigit (ch)) ch = getchar (); while (isdigit (ch)) num = (num << 3) + (num << 1) + ch - '0', ch = getchar ();
return num; }
int main () { T = getnum (); for (int Case = 1; Case <= T; Case ++) { N = getnum (); vcnt = 0; for (int i = 1; i <= N; i ++) { l[i] = getnum (), r[i] = getnum (), value[i] = getnum (); vcol[++ vcnt] = l[i], vcol[++ vcnt] = r[i]; } sort (vcol + 1, vcol + vcnt + 1); vcnt = unique (vcol + 1, vcol + vcnt + 1) - vcol - 1; for (int i = 1; i <= N; i ++) { l[i] = lower_bound (vcol + 1, vcol + vcnt + 1, l[i]) - vcol; r[i] = lower_bound (vcol + 1, vcol + vcnt + 1, r[i]) - vcol; } for (int len = 2; len <= vcnt; len ++) for (int i = 1; i <= vcnt - len + 1; i ++) { int j = i + len - 1; int maxv = - 1, sl, sr; for (int k = 1; k <= N; k ++) if (i <= l[k] && r[k] <= j) if (value[k] > maxv) { maxv = value[k]; sl = l[k], sr = r[k]; } f[i][j] = maxv == - 1 ? 0 : INF; if (maxv != - 1) for (int k = sl; k <= sr; k ++) f[i][j] = min (f[i][j], f[i][k - 1] + f[k + 1][j] + maxv); } printf ("%d\n", f[1][vcnt]); }
return 0; }
|